package br.edu.ufcg.unknown.exception;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;

public class AnalisadorSintaticoUnknownRuntimeException extends UnknownRuntimeException {

	private static final String LINE_SEPARATOR = System.getProperty("line.separator");

	/**
	 * 
	 */
	private static final long serialVersionUID = 3528567430594223860L;

	private String lexemaEncontrado;

	private List<String> lexemasEsperados = new LinkedList<String>();

	private String simboloTerminalOuNaoTerminalEsperado;

	private int linha;

	private int coluna;

	private List<AnalisadorSintaticoUnknownRuntimeException> listaDeErros = new LinkedList<AnalisadorSintaticoUnknownRuntimeException>();

	private AnalisadorSintaticoUnknownRuntimeException(String mensagem, Throwable e) {
		super(mensagem, e);
	}

	private AnalisadorSintaticoUnknownRuntimeException(String mensagem, String lexemaEncontrado) {
		this(mensagem, (Throwable) null);
		this.lexemaEncontrado = lexemaEncontrado;
	}

	public AnalisadorSintaticoUnknownRuntimeException(String lexemaEncontrado, int linha, int coluna,
			String simboloTerminalOuNaoTerminalEsperado) {
		this(lexemaEncontrado);
		this.linha = linha;
		this.coluna = coluna;
		this.simboloTerminalOuNaoTerminalEsperado = simboloTerminalOuNaoTerminalEsperado;
	}

	public AnalisadorSintaticoUnknownRuntimeException(String lexemaEncontrado) {
		this("Erro! Lexema esperado não encontrado", lexemaEncontrado);
	}

	public int getLinha() {
		return linha;
	}

	public int getColuna() {
		return coluna;
	}

	public String getLexemaEncontrado() {
		return lexemaEncontrado;
	}

	public List<String> getLexemasEsperados() {
		return lexemasEsperados;
	}

	public String getSimboloTerminalOuNaoTerminalEsperado() {
		return simboloTerminalOuNaoTerminalEsperado;
	}

	public void addLexemaEsperado(String lexemaEsperado) {
		this.lexemasEsperados.add(lexemaEsperado);
	}

	public void addLexemaEsperado(List<String> lexemasEsperados) {
		for (String lexemaEsperado : lexemasEsperados) {
			this.lexemasEsperados.add(lexemaEsperado);	
		}
	}
	
	public void addErro(AnalisadorSintaticoUnknownRuntimeException e) {
		this.listaDeErros.add(e);
	}

	public List<AnalisadorSintaticoUnknownRuntimeException> getListaDeErros() {
		return this.listaDeErros;
	}

	public int hashCode(){
		return this.toString(new StringBuilder()).hashCode();
	}
	
	public boolean equals(Object obj) {
		AnalisadorSintaticoUnknownRuntimeException asure = (AnalisadorSintaticoUnknownRuntimeException) obj;

		if (this.lexemaEncontrado != null && asure.lexemaEncontrado != null) {
			if (!(this.lexemaEncontrado.equals(asure.lexemaEncontrado))) {
				return false;
			}
		} else if (this.lexemaEncontrado != null || asure.lexemaEncontrado != null) {
			return false;
		}

		if (this.lexemasEsperados.size() != asure.lexemasEsperados.size()) {
			return false;
		}

		if (this.listaDeErros.size() != asure.listaDeErros.size()) {
			return false;
		}

		return CollectionUtils.isEqualCollection(this.lexemasEsperados, asure.lexemasEsperados)
				&& CollectionUtils.isEqualCollection(this.listaDeErros, asure.listaDeErros);
	}

	public String toString() {
		StringBuilder sb = new StringBuilder();

		sb.append("linha: ");
		sb.append(this.linha);
		sb.append(LINE_SEPARATOR);

		sb.append("coluna: ");
		sb.append(this.coluna);
		sb.append(LINE_SEPARATOR);

		toString(sb);
		
		return sb.toString();
	}

	private StringBuilder toString(StringBuilder sb){
		String separator = "";
		
		sb.append("lexema encontrado: ");
		sb.append(this.lexemaEncontrado);
		sb.append(LINE_SEPARATOR);

		sb.append("lexema(s) esperado(s):");
		sb.append(LINE_SEPARATOR);

		//XXX Modificar pra não chamar toda vida!
		Collections.sort(this.lexemasEsperados);
		for (String lexemaEsperado : this.lexemasEsperados) {
			sb.append(separator);
			sb.append(lexemaEsperado);
			separator = " ";
		}
		sb.append(LINE_SEPARATOR);
		
		for (AnalisadorSintaticoUnknownRuntimeException e : this.listaDeErros) {
			if(e != this){
				sb.append(e);
				sb.append(LINE_SEPARATOR);
			}
		}

		return sb;
	}
}
